/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package com.rafkind.crawl.generator;

import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory;

/**
 *
 * @author drafkind
 */
public class Unmarshaller<T> {

  public static <T> T build(InputStream sourceStream, Class<T> targetClass, Map<String, Class> schema)
          throws SAXException, InstantiationException, IllegalAccessException, IOException {

    XMLReader xmlReader = XMLReaderFactory.createXMLReader();
    
    CustomSAXHandler handler = new CustomSAXHandler(schema);
    xmlReader.setContentHandler(handler);

    xmlReader.parse(new InputSource(sourceStream));
    
    return (T) handler.getRemainingObject();
  }

  public static class CustomSAXHandler extends DefaultHandler {
    private Deque<Object> currentObjectStack;
    private Map<String, Class> schema;

    public CustomSAXHandler(Map<String, Class> schema) {
      this.currentObjectStack = new ArrayDeque<Object>();
      this.schema = schema;
    }

    public Object getRemainingObject() {
      return currentObjectStack.peek();
    }

    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
      if (currentObjectStack.size() > 1) {
        try {
          Object endingObject = currentObjectStack.pop();
          Object previousObject = currentObjectStack.peek();


          PropertyDescriptor pd = PropertyUtils.getPropertyDescriptor(previousObject, localName + "s");

          if (pd.getReadMethod().getReturnType().isAssignableFrom(List.class)) {
            List list = (List) pd.getReadMethod().invoke(previousObject);
            list.add(endingObject);
          }

        } catch (Exception ex) {
          ex.printStackTrace();
        }
      }
    }

    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
      Class clazz = schema.get(localName);
      if (clazz != null) {
        try {
          Object o = clazz.newInstance();

          Map<String, String> parameters = new HashMap<String, String>();
          for (int i = 0; i < attributes.getLength(); i++) {
            parameters.put(attributes.getLocalName(i), attributes.getValue(i));            
          }
          BeanUtils.populate(o, parameters);
          currentObjectStack.push(o);
        } catch (Exception ex) {
          ex.printStackTrace();
        }
      }
    }
  }
}